<?php
/**
 * +----------------------------------------------------------------------
 * | 公共函数类
 * +----------------------------------------------------------------------
 *                      _ooOoo_
 *                     o8888888o            | AUTHOR: 杜云
 *                     88" . "88            | EMAIL: 987772927@qq.com
 *                     (| -_- |)            | QQ: 987772927
 *                     O\  =  /O            | WECHAT: 987772927
 *                  ____/`---'\____
 *                .'  \\|     |//  `.
 *               /  \\|||  :  |||//  \
 *              /  _||||| -:- |||||-  \
 *              |   | \\\  -  /// |   |
 *              | \_|  ''\-/''  |   |
 *              \  .-\__  `-`  ___/-. /
 *            ___`. .'  /-.-\  `. . __
 *         ."" '<  `.___\_<|>_/___.'  >'"".
 *        | | :  `- \`.;`\ _ /`;.`/ - ` : | |
 *        \  \ `-.   \_ __\ /__ _/   .-` /  /
 *   ======`-.____`-.___\_____/___.-`____.-'======
 *                      `=-='
 * +----------------------------------------------------------------------
 */
declare(strict_types=1);

use Hyperf\Utils\Coroutine;
use Hyperf\HttpServer\Contract\RequestInterface;
use Hyperf\HttpServer\Contract\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Swoole\Server as SwooleServer;
use Hyperf\Server\ServerFactory;
use Hyperf\Utils\Context;
use Hyperf\Utils\ApplicationContext;
use Hyperf\HttpMessage\Cookie\Cookie as HyperfCookie;
use Psr\Http\Message\ResponseInterface as PsrResponseInterface;
use Hyperf\Contract\SessionInterface;
use Jenssegers\Agent\Agent;
use Hyperf\Contract\StdoutLoggerInterface;
use Hyperf\Utils\Arr;
use Hyperf\Cache\Listener\DeleteListenerEvent;
use Psr\EventDispatcher\EventDispatcherInterface;
use App\Core\Common\Driver\CacheDriver;
use App\Core\Common\Container\Auth;
use App\Core\Common\Container\Ip2Region;
use Psr\Container\ContainerInterface;
use Hyperf\Redis\Redis;
use Hyperf\Logger\LoggerFactory;
use Psr\Log\LoggerInterface;
/**
 * 容器实例
 *
 * @return ContainerInterface
 */

function di(): ContainerInterface
{
    return ApplicationContext::getContainer();
}
/**
 * Dispatch an event and call the listeners.
 *
 * @return mixed|\Psr\EventDispatcher\EventDispatcherInterface
 */
function event()
{
    return di()->get(Psr\EventDispatcher\EventDispatcherInterface::class);
}
/**
 * Redis 客户端实例
 *
 * @return Redis|mixed
 */
function redis()
{
    return di()->get(Redis::class);
}
/**
 * 推送消息到 Redis 订阅中
 *
 * @param string       $chan
 * @param string|array $message
 */
function push_redis_subscribe(string $chan, $message)
{
    redis()->publish($chan, is_string($message) ? $message : json_encode($message));
}
/**
 * Server 实例 基于 Swoole Server
 *
 * @return \Swoole\Coroutine\Server|\Swoole\Server
 */
function server()
{
    return di()->get(ServerFactory::class)->getServer()->getServer();
}
/**
 * 控制台日志
 *
 * @return StdoutLoggerInterface|mixed
 */
function stdout_log()
{
    return di()->get(StdoutLoggerInterface::class);
}
/**
 * 文件日志
 *
 * @param string $name
 * @return LoggerInterface
 */
function logger(string $name = 'APP'): LoggerInterface
{
    return di()->get(LoggerFactory::class)->get($name);
}
if (! function_exists('requestEntry')) {

    /**
     * requestEntry
     * 根据异常返回信息，获取请求入口（模块-控制器-方法）
     * @param array $backTrace
     * @return mixed|string
     */
    function requestEntry(array $backTrace)
    {
        $moduleName = '';
        foreach ($backTrace as $v) {
            if (isset($v['file']) && stripos($v['file'],'CoreMiddleware.php')) {
                $tmp = array_reverse(explode('\\',trim($v['class'])));
                if (substr(strtolower($tmp[0]),-10) == 'controller') {
                    $module = str_replace('controller','',strtolower($tmp[1]));
                    $class = str_replace('controller','',strtolower($tmp[0]));
                    $function = $v['function'];
                    $moduleName = $class.'-'.$function;
                    if ($module) {
                        $moduleName = $module.'-'.$moduleName;
                    }
                    break;
                }
            }
        }
        if (!$moduleName) {
            $request = ApplicationContext::getContainer()->get(RequestInterface::class);
            $uri = $request->getRequestUri();
            $moduleName = str_replace('/','-',ltrim($uri,'/'));
        }
        $moduleName = $moduleName??'hyperf';
        return $moduleName;
    }
}

if (! function_exists('getCoId')) {
    /**
     * getCoId
     * 获取当前协程id
     * @return int
     */
    function getCoId()
    {
        return Coroutine::id();
    }
}

if (! function_exists('getClientInfo')) {
    /**
     * getClientInfo
     * 获取请求客户端信息，获取连接的信息
     * @return mixed
     */
    function getClientInfo()
    {
        // 得从协程上下文取出请求
        $request =  Context::get(ServerRequestInterface::class);
        $server = make(SwooleServer::class);
        return $server->getClientInfo($request->getSwooleRequest()->fd);
    }
}

if (! function_exists('getServerLocalIp')) {
    /**
     * getServerLocalIp
     * 获取服务端内网ip地址
     * @return string
     */
    function getServerLocalIp()
    {
        $ip = '127.0.0.1';
        $ips = array_values(swoole_get_local_ip());
        foreach ($ips as $v) {
            if ($v && $v != $ip) {
                $ip = $v;
                break;
            }
        }

        return $ip;
    }
}

if (! function_exists('setCookies')) {
    /**
     * setCookie
     * 设置cookie
     * @param string $key
     * @param string $value
     * @param int $expire
     * @param string $path
     * @param string $domain
     * @param bool $secure
     * @param bool $httpOnly
     * @param bool $raw
     * @param null|string $sameSite
     */
    function setCookies(string $key, $value = '', $expire = 0, string $path = '/', string $domain = '', bool $secure = false, bool $httpOnly = true, bool $raw = false, ?string $sameSite = null)
    {
        // convert expiration time to a Unix timestamp
        if ($expire instanceof \DateTimeInterface) {
            $expire = $expire->format('U');
        } elseif (! is_numeric($expire)) {
            $expire = strtotime($expire);
            if ($expire === false) {
                throw new \RuntimeException('The cookie expiration time is not valid.');
            }
        }
        $response = ApplicationContext::getContainer()->get(ResponseInterface::class);
        $cookie = new HyperfCookie($key, (string)$value, $expire, $path, $domain, $secure, $httpOnly, $raw, $sameSite);
        $response = $response->withCookie($cookie);
        Context::set(PsrResponseInterface::class, $response);
        return;
    }
}

if (! function_exists('getCookie')) {
    /**
     * getCookie
     * 获取cookie
     * @param string $key
     * @param null|string $default
     * @return mixed
     */
    function getCookie(string $key,?string $default = null)
    {
        $request = ApplicationContext::getContainer()->get(RequestInterface::class);
        return $request->cookie($key, $default);
    }
}

if (! function_exists('hasCookie')) {
    /**
     * hasCookie
     * 判断cookie是否存在
     * @param string $key
     * @return mixed
     */
    function hasCookie(string $key)
    {
        $request = ApplicationContext::getContainer()->get(RequestInterface::class);
        return $request->hasCookie($key);
    }
}

if (! function_exists('delCookie')) {
    /**
     * delCookie
     * 删除cookie
     * @param string $key
     * @return bool
     */
    function delCookie(string $key) :bool
    {
        if (!hasCookie($key)) {
            return false;
        }

        setCookies($key,'', time()-1);

        return true;
    }
}

if (! function_exists('setSessionId')) {
    /**
     * setSessionId
     * 设置sessionid
     * @param string $id
     */
    function setSessionId(string $id)
    {
        $session = ApplicationContext::getContainer()->get(SessionInterface::class);
        $session->setId($id);
        return ;
    }
}

if (! function_exists('getSessionId')) {
    /**
     * getSessionId
     * 获取sessionid
     */
    function getSessionId()
    {
        $session =  ApplicationContext::getContainer()->get(SessionInterface::class);
        return $session->getId();
    }
}

if (! function_exists('setSession')) {
    /**
     * setSession
     * 设置session
     * @param string $k
     * @param $v
     */
    function setSession(string $k,$v)
    {
        $session = ApplicationContext::getContainer()->get(SessionInterface::class);
        $session->set($k,$v);
        return ;
    }
}

if (! function_exists('getSession')) {
    /**
     * getSession
     * 获取session
     * @param string $k
     * @param null $default
     * @return mixed
     */
    function getSession(string $k,$default = null)
    {
        $session = ApplicationContext::getContainer()->get(SessionInterface::class);
        return $session->get($k,$default);
    }
}

if (! function_exists('getAllSession')) {
    /**
     * getAllSession
     * 获取所有session
     * @return mixed
     */
    function getAllSession()
    {
        $session = ApplicationContext::getContainer()->get(SessionInterface::class);
        return $session->all();
    }
}


if (! function_exists('hasSession')) {
    /**
     * hasSession
     * 判断session是否存在
     * @param string $name
     * @return bool
     */
    function hasSession(string $name)
    {
        $session = ApplicationContext::getContainer()->get(SessionInterface::class);
        return $session->has($name);
    }
}

if (! function_exists('removeSession')) {
    /**
     * removeSession
     * 从 Session 中获取并删除一条数据
     * @param string $name
     * @return mixed
     */
    function removeSession(string $name)
    {
        $session = ApplicationContext::getContainer()->get(SessionInterface::class);
        return $session->remove($name);
    }
}

if (! function_exists('forgetSession')) {
    /**
     * forgetSession
     * 从session中删除一条或多条数据
     * @param $keys string|array
     */
    function forgetSession($keys)
    {
        $session = ApplicationContext::getContainer()->get(SessionInterface::class);
        $session->forget($keys);
        return;
    }
}

if (! function_exists('clearSession')) {
    /**
     * clearSession
     * 清空当前 Session 里的所有数据
     */
    function clearSession()
    {
        $session = ApplicationContext::getContainer()->get(SessionInterface::class);
        return $session->clear();
    }
}

if (! function_exists('destroySession')) {
    /**
     * destroySession
     * 销毁session
     */
    function destroySession()
    {
        $session = ApplicationContext::getContainer()->get(SessionInterface::class);
        return $session->invalidate();
    }
}

if (! function_exists('getIpLocation')) {
    /**
     * getIpLocation
     * 获取ip对应的城市信息
     * @param $ip
     * @return mixed
     */
    function getIpLocation($ip)
    {
        $dbFile = BASE_PATH . '/app/Core/Common/Container/ip2region.db';
        $ip2regionObj = new Ip2Region($dbFile);
        $ret = $ip2regionObj->binarySearch($ip);
        return $ret;
    }
}

if (! function_exists('isStdoutLog')) {
    /**
     * isStdoutLog
     * 判断日志类型是否允许输出
     * @param string $level
     * @return bool
     */
    function isStdoutLog(string $level)
    {
        $config = config(StdoutLoggerInterface::class, ['log_level' => []]);
        return in_array(strtolower($level), $config['log_level'], true);
    }
}

if (! function_exists('isMobileNum')) {
    /**
     * isMobileNum
     * 判断是否为手机号
     * @param $v
     * @return bool
     */
    function isMobileNum($v)
    {
        $search = '/^0?1[3-9][0-9]\d{8}$/';
        if (preg_match($search, $v)) {
            return true;
        } else {
            return false;
        }
    }
}

if (! function_exists('encryptPassword')) {
    /**
     * encryptPassword
     * 加密密码
     * @param string $password 用户输入的密码
     * @access public
     * @return void
     */
    function encryptPassword($password)
    {
        return password_hash($password, PASSWORD_DEFAULT);
    }
}

if (! function_exists('checkPassword')) {
    /**
     * checkPassword
     * 检测密码
     * @param $value
     * @param $hashedValue
     * @return bool
     */
    function checkPassword($value, $hashedValue)
    {
        if (strlen($hashedValue) === 0) {
            return false;
        }

        return password_verify($value, $hashedValue);
    }
}

if (! function_exists('getUserUniqueId')) {
    /**
     * getUserUniqueId
     * 获取用户唯一标示，用户ID生成规则，32位
     * @access public
     * @return string
     * @throws Exception
     */
    function getUserUniqueId()
    {
        // 前缀3位
        $prefix = config('app_uid_prefix');
        $prefix = substr($prefix, 0, 3);
        //随机字符串14位
        $rand = substr(str_replace(['/', '+', '='], '', base64_encode(random_bytes(14))), 0, 14);
        //根据当前时间生成的随机字符串11位
        $uniqid = substr(uniqid(), 2);
        //当前服务器ip后4位
        $ip = getServerLocalIp();
        $ipList = explode('.', $ip);
        if(empty($ipList) || count($ipList) < 4 ){
            $ipStr = '01';
        }else{
            $ipStr = $ipList[2].$ipList[3];
        }
        $ip = dechex($ipStr);
        $ip = str_pad($ip, 6, 'f', STR_PAD_LEFT);
        if(PHP_SAPI != 'cli'){
            $ip = substr($ip, -4);
        }else{
            $ip = 'z'.substr($ip, -3);
        }

        //总共32位字符串
        return strtolower($prefix.$ip.$rand.$uniqid);
    }
}

if(!function_exists('ListToTree')) {
    /**
     * 采用递归将数据列表转换成树
     *
     * @param $dataArr           数据列表
     * @param int $rootId        根节点ID
     * @param string $pkName     主键名
     * @param string $pIdName    父节点id名
     * @param string $childName  子节点名称
     * @return array
     */
    function ListToTree($dataArr, $rootId = 0,$depth=0, $pkName = 'id', $pIdName = 'pid', $childName = 'children',$d_sub='depth'):array
    {
        $arr = [];
        foreach ($dataArr as $sorData) {
            if ($sorData[$pIdName] == $rootId) {
                $sorData[$d_sub] = $depth;
                $children = ListToTree($dataArr, $sorData[$pkName],$depth+1);
                $sorData[$childName] = $children;
                $arr[] = $sorData;
            }
        }
        return $arr;
    }
}

if (! function_exists('array_pluck')) {
    /**
     * Pluck an array of values from an array.
     * 从数组中提取值组成新数组
     *
     * @param  array   $array
     * @param  string|array  $value
     * @param  string|array|null  $key
     * @return array
     */
    function array_pluck($array, $value, $key = null)
    {
        return Arr::pluck($array, $value, $key);
    }
}

if (! function_exists('flushAnnotationCache')) {
    /**
     * flushAnnotationCache
     * 刷新注解缓存，清楚注解缓存
     * @param string $listener
     * @param array $keys
     * @return bool
     */
    function flushAnnotationCache($listener = '', $keys = [])
    {
        if (!$listener || !$keys) throw new \RuntimeException('参数不正确！');
        $keys = is_array($keys)?$keys:[$keys];
        foreach ($keys as $key) event()->dispatch(new DeleteListenerEvent($listener, [$key]));
        return true;
    }
}

if (! function_exists('clearCache')) {
    /**
     * clearCache
     * 清空当前 缓存
     */
    function clearCache()
    {
        $config = config('cache.default');
        $cache = make(CacheDriver::class,['config'=>$config]);
        return $cache->clear();
    }
}

if (! function_exists('delCache')) {
    /**
     * delCache
     * 删除缓存，1条/多条
     * @param array $keys
     * @return bool
     * @throws \Psr\SimpleCache\InvalidArgumentException
     */
    function delCache($keys = [])
    {
        $config = config('cache.default');
        $cache = make(CacheDriver::class,['config'=>$config]);
        if (is_array($keys)) {
            $cache->deleteMultiple($keys);
        } else {
            $cache->delete($keys);
        }

        return true;
    }
}

if (! function_exists('clearPrefixCache')) {
    /**
     * clearPrefixCache
     * 根据前缀清楚缓存
     * 函数的含义说明
     * @param string $prefix
     * @return bool
     */
    function clearPrefixCache($prefix = '')
    {
        $config = config('cache.default');
        $cache = make(CacheDriver::class,['config'=>$config]);
        $cache->clearPrefix($prefix);
        return true;
    }
}

if (! function_exists('setCache')) {
    /**
     * setCache
     * 设置缓存
     * @param $key
     * @param $value
     * @param null $ttl
     * @return bool
     * @throws \Psr\SimpleCache\InvalidArgumentException
     */
    function setCache($key, $value, $ttl = null)
    {
        $config = config('cache.default');
        $cache = make(CacheDriver::class,['config'=>$config]);
        return $cache->set($key, $value, $ttl);
    }
}

if (! function_exists('setMultipleCache')) {
    /**
     * setMultipleCache
     * 批量设置缓存
     * @param $values
     * @param null $ttl
     * @return bool
     * @throws \Psr\SimpleCache\InvalidArgumentException
     */
    function setMultipleCache($values, $ttl = null)
    {
        $config = config('cache.default');
        $cache = make(CacheDriver::class,['config'=>$config]);
        return $cache->setMultiple($values, $ttl);
    }
}

if (! function_exists('getCache')) {
    /**
     * getCache
     * 获取缓存
     * @param $key
     * @param null $default
     * @return iterable
     * @throws \Psr\SimpleCache\InvalidArgumentException
     */
    function getCache($key, $default = null)
    {
        $config = config('cache.default');
        $cache = make(CacheDriver::class,['config'=>$config]);
        return $cache->get($key, $default);
    }
}

if (! function_exists('getMultipleCache')) {
    /**
     * getMultipleCache
     * 获取多个缓存
     * @param array $keys
     * @param null $default
     * @return iterable
     * @throws \Psr\SimpleCache\InvalidArgumentException
     */
    function getMultipleCache(array $keys, $default = null)
    {
        $config = config('cache.default');
        $cache = make(CacheDriver::class,['config'=>$config]);
        return $cache->getMultiple($keys, $default);
    }
}

if (!function_exists('formatBytes')) {
    /**
     * formatBytes
     * 字节->兆转换
     * 字节格式化
     * @param $bytes
     * @return string
     */
    function formatBytes($bytes)
    {
        if($bytes >= 1073741824) {
            $bytes = round($bytes / 1073741824 * 100) / 100 . 'GB';
        } elseif($bytes >= 1048576) {
            $bytes = round($bytes / 1048576 * 100) / 100 . 'MB';
        } elseif($bytes >= 1024) {
            $bytes = round($bytes / 1024 * 100) / 100 . 'KB';
        } else {
            $bytes = $bytes . 'Bytes';
        }
        return $bytes;
    }
}

if (!function_exists('durationFormat')) {
    /**
     * durationFormat
     * 时间格式化，格式化秒
     * @param $number
     * @return string
     */
    function durationFormat($number)
    {
        if (! $number) {
            return '0分钟';
        }
        $newTime = '';
        if (floor($number/3600) > 0) {
            $newTime .= floor($number/3600).'小时';
            $number = $number%3600;
        }
        if ($number/60 > 0) {
            $newTime .= floor($number/60).'分钟';
            $number = $number%60;
        }
        if ($number < 60) {
            $newTime .= $number.'秒';
        }
        return $newTime;
    }
}
if (!function_exists('get_sex')) {
    /**
     *  根据身份证号码获取性别
     *  author:xiaochuan
     *  @param string $idcard    身份证号码
     *  @return int $sex 性别 1男 2女 0未知
     */
    function get_sex($idcard) {
        if(empty($idcard)) return null; 
        $sexint = (int) substr($idcard, 16, 1);
        return $sexint % 2 === 0 ? '女' : '男';
    }
}
if (!function_exists('get_birthday')) {
    /**
     *  根据身份证号码获取生日
     *  author:xiaochuan
     *  @param string $idcard    身份证号码
     *  @return $birthday
     */
    function get_birthday($idcard) {
        if(empty($idcard)) return null; 
        $bir = substr($idcard, 6, 8);
        $year = (int) substr($bir, 0, 4);
        $month = (int) substr($bir, 4, 2);
        $day = (int) substr($bir, 6, 2);
        return $year . "-" . $month . "-" . $day;
    }
}
if (!function_exists('get_age')) {
    /**
     *  根据身份证号码计算年龄
     *  author:xiaochuan
     *  @param string $idcard    身份证号码
     *  @return int $age
     */
    function get_age($idcard){  
        if(empty($idcard)) return null; 
        #  获得出生年月日的时间戳 
        $date = strtotime(substr($idcard,6,8));
        #  获得今日的时间戳 
        $today = strtotime('today');
        #  得到两个日期相差的大体年数 
        $diff = floor(($today-$date)/86400/365);
        #  strtotime加上这个年数后得到那日的时间戳后与今日的时间戳相比 
        $age = strtotime(substr($idcard,6,8).' +'.$diff.'years')>$today?($diff+1):$diff; 
        return $age; 
    }
}
if (!function_exists('get_address')) {
    /**
     *  根据身份证号码获取出身地址
     *  author:xiaochuan
     *  @param string $idcard    身份证号码
     *  @return string $address
     */
    function get_address($idcard, $type=1){
        if(empty($idcard)) return null;
        $address = include('./address.php');
        switch ($type) {
            case 1:
                    # 截取前六位数(获取基体到县区的地址)
                    $key = substr($idcard,0,6);
                    if(!empty($address[$key])) return $address[$key];
                    # 截取前两位数(没有基体到县区的地址就获取省份)
                    $key = substr($idcard,0,2); 
                    if(!empty($address[$key])) return $address[$key];
                    # 都没有
                    return '未知地址';
                break;
            case 2:
                    # 截取前两位数(只获取省份)
                    $key = substr($idcard,0,2); 
                    if(!empty($address[$key])) return $address[$key];
                break;
            default:
                    return null;
                break;
        }
    }
}
if (!function_exists('base64_image_content')) {
    /**
     * base64_image_content
     * base64转图片
     * @return string
     */
    function base64_image_content($base64_image_content = '',$file_name = ''){
        if(!$base64_image_content || !$file_name) return '';
        $uploadDir = 'uploads/' . date('Ymd');
        $new_file = "{$uploadDir}/".$file_name;
        if (!file_exists('public/'.$uploadDir)) @mkdir('public/'.$uploadDir, 0644, true);
        if (file_put_contents('public/'.$new_file, base64_decode($base64_image_content))){
            return $new_file;
        }else{
            return '';
        }
    }
}

if (!function_exists('createDateRange')) {
    /**
     * Returns every date between two dates as an array
     * @param string $startDate the start of the date range
     * @param string $endDate the end of the date range
     * @param string $format DateTime format, default is Y-m-d
     * @return array returns every date between $startDate and $endDate, formatted as "Y-m-d"
     */
    function createDateRange($startDate, $endDate, $format = "Y-m-d")
    {
        $begin = new DateTime($startDate);
        $end = new DateTime($endDate);
        $interval = new DateInterval('P1D'); // 1 Day
        $dateRange = new DatePeriod($begin, $interval, $end);
        $range = [];
        foreach ($dateRange as $date) $range[] = $date->format($format);
        return $range;
    }
}
if (!function_exists('MonthEveryDay')) {
    /*
     * 当月每天的日期数组
     */
    function MonthEveryDay()
    {
        $startDate = date('Y-m-01'); // 获取当月的第一天日期
        $endDate = date('Y-m-t'); // 获取当月的最后一天日期
        $dates = [];
        $currentDate = $startDate;
        while (strtotime($currentDate) <= strtotime($endDate)) {
            $dates[] = $currentDate;
            $currentDate = date('Y-m-d', strtotime('+1 day', strtotime($currentDate)));
        }
        return $dates;
    }
}
if (!function_exists('getDatesBetweenTwoDays')) {
    /*
     * 返回这两个日期之间的所有日期
     */
    function getDatesBetweenTwoDays($startDate, $endDate) {
        $start = new DateTime($startDate);
        $end = new DateTime($endDate);
        $end->add(new DateInterval('P1D')); // 添加一天以包括结束日期
        $interval = new DateInterval('P1D');
        $period = new DatePeriod($start, $interval, $end);
     
        $dates = [];
        foreach ($period as $date) {
            $dates[] = $date->format('Y-m-d');
        }
        return $dates;
    }
}
if (!function_exists('getInterval')) {
    /*
     * 返回这两个日期之间的时差
     */
    function getInterval($timestamp1, $timestamp2) {
        $datetime1 = new DateTime();
        $datetime1->setTimestamp($timestamp1);
        
        $datetime2 = new DateTime();
        $datetime2->setTimestamp($timestamp2);
        
        $interval = $datetime1->diff($datetime2);
        
        return array(
            'days' => $interval->format('%a'), // 相差的天数
            'hours' => $interval->h, // 相差的小时数
            'minutes' => $interval->i, // 相差的分钟数
        );
    }
}
if (!function_exists('isPasswordComplex')) {
    /*
     * 返回这两个日期之间的时差
     */
    function isPasswordComplex($password) {
        // 密码长度至少为 8 个字符
        $length = strlen($password) >= 8;
        // 包含至少一个大写字母
        $hasUpper = preg_match('/[A-Z]/', $password);
        // 包含至少一个小写字母
        $hasLower = preg_match('/[a-z]/', $password);
        // 包含至少一个数字
        $hasNumber = preg_match('/\d/', $password);
        // 包含至少一个特殊字符
        $hasSpecial = preg_match('/[!@#$%^&_*]/', $password);
        // 返回所有条件是否都满足
        return $length && $hasUpper && $hasLower && $hasNumber && $hasSpecial;
    }
}
if (!function_exists('getRelevantTables')) {
    /**
     * 根据传入的开始时间和结束时间，判断所占用的表数组
     *
     * @param string $startTime 开始时间 (格式: Y-m-d H:i:s)
     * @param string $endTime 结束时间 (格式: Y-m-d H:i:s)
     * @param array $tableTimeRanges 表时间范围数组
     * @return array 包含所占用表名的数组
     */
    function getRelevantTables($startTime, $endTime, $tableTimeRanges) {
        // 将输入的时间转换为时间戳
        $startTimestamp = strtotime($startTime);
        $endTimestamp = strtotime($endTime);

        $relevantTables = [];

        foreach ($tableTimeRanges as $tableName => $timeRange) {
            // 获取当前表的时间范围的时间戳
            $tableStart = strtotime($timeRange['start']);
            $tableEnd = strtotime($timeRange['end']);

            // 检查时间范围是否与当前表的时间范围有重叠
            if ($startTimestamp > $tableStart && $startTimestamp < $tableEnd) {
                $relevantTables[] = $tableName;
            }
            // 检查时间范围是否与当前表的时间范围有重叠
            if ($endTimestamp > $tableStart && $endTimestamp < $tableEnd) {
                $relevantTables[] = $tableName;
            }
        }

        return array_map('unserialize', array_unique(array_map('serialize', $relevantTables)));
    }
}

function haversineGreatCircleDistance($latitudeFrom, $longitudeFrom, $latitudeTo, $longitudeTo, $earthRadius = 6371) {
    // 将角度转换为弧度
    $latFrom = deg2rad($latitudeFrom);
    $lonFrom = deg2rad($longitudeFrom);
    $latTo = deg2rad($latitudeTo);
    $lonTo = deg2rad($longitudeTo);

    $latDelta = $latTo - $latFrom;
    $lonDelta = $lonTo - $lonFrom;

    $angle = 2 * asin(sqrt(pow(sin($latDelta / 2), 2) + cos($latFrom) * cos($latTo) * pow(sin($lonDelta / 2), 2)));
    return $angle * $earthRadius;
}

function generateRandomPointWithinDistance($latitude, $longitude, $maxDistanceInKm) {
    $earthRadiusKm = 6371;
    $maxAngle = $maxDistanceInKm / $earthRadiusKm;

    $latitudeDelta = $maxAngle * (2 * M_PI / 360);
    $longitudeDelta = $maxAngle * (2 * M_PI / 360) / cos(deg2rad($latitude));

    $newLatitude = $latitude + $latitudeDelta * (2 * rand() / getrandmax() - 1);
    $newLongitude = $longitude + $longitudeDelta * (2 * rand() / getrandmax() - 1);

    return array($newLatitude, $newLongitude);
}

function generateRoute($startLatitude,$startLongitude,$numPoints, $maxDistanceInKm) {
    $route = array(array($startLatitude, $startLongitude));
    for ($i = 1; $i < $numPoints; $i++) {
        do {
            list($newLatitude, $newLongitude) = generateRandomPointWithinDistance($route[$i - 1][0], $route[$i - 1][1], $maxDistanceInKm);
        } while (haversineGreatCircleDistance($route[$i - 1][0], $route[$i - 1][1], $newLatitude, $newLongitude) > $maxDistanceInKm * 1000); // Convert to meters

        $route[] = array($newLatitude, $newLongitude);
    }
    return $route;
}
